import os
import web
import logging
import sys
import json
import subprocess
import shutil
import webutils


sys.path.append("../db")
sys.path.append("../processor")

import rnetdb
import tcpserial
import keros

from rnetdb import Device
from os import listdir
from os.path import isfile, join, dirname
from webutils import web_session_check

# Setup logging

logging.basicConfig()
logging.getLogger().setLevel(logging.INFO)

# Connect to database
logging.info("Using database SQLLITE3")
db = web.database(dbn='sqlite', db='../db/rnet.db')
rnetdb.setup(db)

# Web.py init
webutils.web_init(globals())

# Session
webutils.web_init_session()

# HTTPS
#webutils.web_init_https()

# Render templates

webutils.web_init_templates()

# Classes

class index:
    def GET(self):
        web_session_check()
        web.header('Content-Type', 'text/html')
        return webutils.page_render.generic("Index","Very first page","Welcome")

class login:
    def GET(self):
        x = web.input(user=None, pwd=None, action="")

        # inform the user that has logged out
        if x.action == "logout" and webutils.session.user is not None:
            webutils.session.kill()
            return webutils.render.login("You have been logged out "+webutils.session.user+"!")

        # if no credentials are passed, ask for them
        if x.user is None or x.pwd is None:
            return webutils.render.login("Please type in your login credentials:")

        # Check credentials
        # TODO: temp
        if x.user != "madlex" or x.pwd != "maddog":
            return webutils.render.login("Wrong passowrd or username, please retry!:")

        webutils.session.user = "madlex"
        return web.seeother('/')

    def POST(self):
        return self.GET()

class home:
    def GET(self):
        web_session_check()
        web.header('Content-Type', 'text/html')
        return webutils.page_render.generic("Home","Welcome page","Welcome home")


class test:
    def GET(self):
        web_session_check()
        web.header('Content-Type', 'text/html')
        content = "YourIP: " + web.ctx.ip
        return webutils.page_render.generic("Test page","Test page",content)

class admin:
    def GET(self):
        return webutils.page_render.generic("Administration","Here you can setup the system","Welcome")

class config_dev_keros:
    def GET(self):
        web_session_check()
        web.header('Content-Type', 'text/html')

        # load categs
        with open("keros_config_categs.json","r") as inf:
            categs = json.load(inf)

        # todo: will this close properly
        try:
            client = keros.RemoteClient(tcpserial.TcpCmdClient())
            for categ, map in categs:
                map["default"] = client.getConfig(categ)
            client.close()
        except:
            pass

        # load commands
        with open("keros_commands.json","r") as inf:
            commands = json.load(inf)

        # todo: doesn't work
        try:
            with open("keros_user_commands.json","r") as inf:
                user_commands = json.load(inf)
                commands.append(user_commands)
        except:
            pass

        return webutils.page_render.config_dev_keros(commands, categs)

    def POST(self):
        web_session_check()
        x = web.input(addcmd=None)

        #
        # Add new command
        #
        if x.get("addcmd") != None:
            name = x.addcmd
            desc = x.get("desc")
            cmd = x.get("cmd")

            if desc == None or cmd == None:
                return "Failed: invalid arguments"

            try:
                # read previous set of commands
                with open("keros_user_commands.json","r") as inf:
                    commands = json.load(inf)

                # check for duplicate command names
                for xn, xdesc, xcmd in commands[1]:
                    if xn == name:
                        return "Failed: duplicated command name"

                # append posted one
                commands[1].append([name,desc,cmd])

                # save the merged results
                with open("keros_user_commands.json","w") as outf:
                    json.dump(commands,outf,indent=4)
            except BaseException as e:
                return "Failed: " + str(e)

            # Command added
            return "Ok, command '%s' added" % name
        elif x.get("rmcmd") != None:
            name = x.rmcmd

            try:
                # read previous set of commands
                with open("keros_user_commands.json","r") as inf:
                    commands = json.load(inf)

                # search command and destroy it
                index = 0
                for xn, xdesc, xcmd in commands[1]:
                    if xn == name:
                        del commands[1][index]
                        break
                    index = index + 1

                # save the merged results
                with open("keros_user_commands.json","w") as outf:
                    json.dump(commands,outf,indent=4)
            except BaseException as e:
                return "Failed: " + str(e)

            return "Ok, removed '%s'" % name

        return "Failed: invalid post"

class camera:
    def GET(self):
        web_session_check()
        web.header('Content-Type', 'text/html')

         # load form options
        with open("raspistill_form_options.json","r") as inf:
            cameraopts = json.load(inf)

        # load saved defaults
        with open("raspistill_options.json","r") as inf:
            defaults = json.load(inf)

        return webutils.page_render.camera(cameraopts,defaults)

    def POST(self):
        web_session_check()
        x = web.input(action=None)

        if x.action == None:
            return "Failed: no action given"

        #
        # camera action
        #
        if x.action == "capture":
            try:
                # loading capture options from json
                with open("raspistill_options.json","r") as inf:
                    args = json.load(inf)

                args = self.toRaspistillCallArgs(args)

                retcode = subprocess.call(args)
                if retcode < 0:
                    return "Failed: child was terminated by signal %d", -retcode
                else:
                    return "Ok"
            except OSError as e:
                return "Failed: " + str(e)
            except BaseException as e:
                return "Failed: " + str(e)

        #
        # save camera options when changed
        #
        if x.action == "setopt":
            try:
                # remove action element and serialize the rest of input
                del x["action"]
                with open("raspistill_options.json","w") as outf:
                    json.dump(x,outf,indent=4)
            except BaseException as e:
                return "Failed:" + str(e)

            return "Ok, camera options modified"

        #
        # reset options to defaults
        #
        if x.action == "optreset":
            try:
                shutil.copy("raspistill_options_original.json","raspistill_options.json")
            except BaseException as e:
                return "Failed: " + str(e)

            return "Ok, defaults restored"

        return "Failed: invalid action"


    def toRaspistillCallArgs(self,argmap):
        # first arg is the command name
        args = ["raspistill"]

        # translate dict helps converting names from json
        # to real argument names for raspistill
        translate = {
            "exposure": "ex",
            "awb" : "awb",
            "ifx" : "ifx",
            "mtering" : "mm",
            "quality" : "q",
            "encoding" : "e",
            "brightness" : "br",
            "sharpness" : "sh",
            "contrast" : "co",
            "saturation" : "sa"
        }

        for key, val in argmap.iteritems():
            # Special argument - image size
            if key == "size":
                width, height = val.split("x")
                args.append("-w")
                args.append(width)
                args.append("-h")
                args.append(height)
            # special argument - timeout
            elif key =="timeout":
                tout = int(val)*1000
                if tout == 0: tout = 10 # don't allow 0 as timeout
                args.append("-t")
                args.append(str(tout))
            # search argument in translation map
            else:
                argname = translate.get(key,None)
                if argname != None:
                    args.append("-"+argname)
                    args.append(val)

        # append picture location
        args.append("-o")
        args.append("static/camera/latest.jpg")

        return args

class devices:
    def GET(self):
        web_session_check()
        web.header('Content-Type', 'text/html')
        devices = rnetdb.list_devices()
        return webutils.page_render.devices(devices)

class device_info:
    def GET(self):
        web_session_check()
        web.header('Content-Type', 'text/html')
        x = web.input(byid=None)

        # Needing device id
        if x.byid == None:
            raise web.BadRequest()

        # Create device object
        dev = Device(int(x.byid))
        return webutils.render.device_info(dev)

class measurements:
    def GET(self):
        web_session_check()
        web.header('Content-Type', 'text/html')

        devs = db.query(
            "SELECT DISTINCT DEVICES.id, DEVICES.name FROM DEVICES "
            "INNER JOIN DEVICE_CAPS ON (DEVICES.id = DEVICE_CAPS.device)" )

        units = db.query("SELECT id, name FROM UNITS")
        locations = db.query("SELECT id, name from LOCATIONS")

        return webutils.page_render.measurements(devs, units, locations)

class command:
    def GET(self):
        web_session_check()
        x = web.input(cmd=None)
        if x.cmd is None:
            raise web.badrequest()

        response = None
        while True:
            client = tcpserial.TcpCmdClient()

            # open
            if client.open() == False:
                response = client.error
                if response == None or len(response) == 0:
                    response = "Failed: could not connect to server"
                client.close()
                break

            # run
            response = client.command(x.cmd)
            if response == None:
                response = client.error
                if response == None or len(response) == 0:
                    response = "Failed: could not send command"
                client.close()
                break

            # done
            break

        return response

class upload:
    def GET(self):
        web_session_check()
        """ Get filenames listed on /static/uploads """
        files = [f for f in listdir(webutils.upload_directory) \
                 if isfile(join(webutils.upload_directory, f)) and f[0] != '.']
        return webutils.page_render.upload(files)

    def POST(self):
        web_session_check()
        x = web.input(upfile={})

        if x.has_key('deletefile'):
            os.remove(join(webutils.upload_directory, x.deletefile))

            # reload
            raise web.seeother(web.ctx["path"])

        if type(x.upfile) != type(dict):
            up_path = join(webutils.upload_directory, x.upfile.filename)
            f = open(up_path, "wb")
            content = x.upfile.file.read()
            f.write(content)
            f.close()

        # reload
        raise web.seeother(web.ctx["path"])



# Run application
if __name__ == "__main__":
    webutils.app.run()
